﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Aliyun.OSS;
using Aliyun.OSS.Common;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace AliyunOSServiceLib.Services
{
    public class AliyunOSService : IAliyunOSService
    {
        private readonly AliyunOSServiceSettins _settings;
        private readonly OssClient _ossClient = null;
        private readonly ILogger<AliyunOSService> _logger;
        static AutoResetEvent _event = new AutoResetEvent(false);
        public AliyunOSService(IOptionsSnapshot<AliyunOSServiceSettins> settings, ILogger<AliyunOSService> logger)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _settings = settings.Value ?? throw new ArgumentNullException(nameof(settings));
            if (_ossClient == null)
            {
                var conf = new ClientConfiguration
                {
                    ConnectionTimeout = _settings.ConnectionTimeout,
                    MaxErrorRetry = _settings.MaxErrorRetry,
                    IsCname = _settings.IsCname,//是否启用自定义域名绑定到存储空间
                    EnalbeMD5Check = false //MD5检验，默认关闭
                };
                ClientConfiguration.ConnectionLimit = _settings.ConnectionLimit;
                _ossClient =new OssClient(_settings.Endpoint, _settings.AccessKeyId, _settings.AccessKeySecret, conf);
            }
        }
        /// <summary>
        /// 创建存储空间
        /// </summary>
        /// <returns></returns>
        public Bucket CreateBucket()
        {
            try
            {
               return _ossClient.CreateBucket(_settings.BucketName);
            }
            catch(Exception e)
            {
                throw e;
            }
        }
        /// <summary>
        /// 列举存储空间
        /// </summary>
        public IEnumerable<Bucket> ListBuckets()
        {
            try
            {
                return _ossClient.ListBuckets();
            }
            catch(Exception e)
            {
                throw e;
            }
        }
        /// <summary>
        /// 判断存储空间是否存在
        /// </summary>
        /// <returns></returns>
        public bool DoesBucketExist()
        {
            try
            {
                return  _ossClient.DoesBucketExist(_settings.BucketName);
            }
            catch(Exception e)
            {
                throw e;
            }
        }
        /// <summary>
        /// 设置存储空间访问权限
        /// </summary>
        /// <param name="i">0:Private 1:PublicRead 2:PublicReadWrite 3:Default</param>
        /// <returns></returns>
        public bool SetBucketAcl(CannedAccessControlList accessEnum)
        {
            try
            {
                _ossClient.SetBucketAcl(_settings.BucketName, accessEnum);
                return true;
            }
            catch(Exception e)
            {
                _logger.LogError(e.Message);
                return false;
            }
        }
        /// <summary>
        /// 获取存储空间权限
        /// </summary>
        /// <returns></returns>
        public AccessControlList GetBucketAcl()
        {
            try
            {
                return _ossClient.GetBucketAcl(_settings.BucketName);
            }
            catch(Exception e)
            {
                throw e;
            }
        }
        /// <summary>
        /// 删除存储空间
        /// </summary>
        /// <returns></returns>
        public bool DeleteBucket()
        {
            try
            {
                _ossClient.DeleteBucket(_settings.BucketName);
                return true;
            }
            catch(Exception e)
            {
                _logger.LogError(e.Message);
                return false;
            }
        }
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="stream">文件流</param>
        /// <param name="key">表示您上传文件时需要指定的文件名称，如abc/efg/123.jpg</param>
        /// <returns></returns>
        public bool PutObject(Stream stream, string objectName)
        {
            try
            {
                _ossClient.PutObject(_settings.BucketName, objectName, stream);
                stream.Close();
                return true;
            }
            catch(Exception e)
            {
                _logger.LogError(e.Message);
                return false;
            }
        }
        /// <summary>
        /// 异步上传文件
        /// </summary>
        /// <param name="stream">文件流</param>
        /// <param name="key">表示您上传文件时需要指定的文件名称，如abc/efg/123.jpg</param>
        /// <returns></returns>
        public bool PutObjectAsync(Stream stream,string objectName)
        {
            try
            {
                bool returnResult = false;
                var metadata = new ObjectMetadata();
                metadata.CacheControl = "No-Cache";
                metadata.ContentType = "text/html";
                string result = "Notice user: put object finish";
                _ossClient.BeginPutObject(_settings.BucketName, objectName, stream,metadata, new AsyncCallback((t) =>
                 {
                     try
                     {
                         _ossClient.EndPutObject(t);
                         _logger.LogInformation(t.AsyncState.ToString());
                         returnResult = true;
                     }
                     catch(Exception e)
                     {
                         _logger.LogError(e.Message);
                     }
                     finally
                     {
                         _event.Set();
                     }
                 }), result);
                _event.WaitOne();
                return returnResult;
            }
            catch(Exception e)
            {
                _logger.LogError(e.Message);
                return false;
            }
        }
        /// <summary>
        /// 下载文件到本地
        /// </summary>
        /// <param name="objectName">表示您在下载文件时需要指定的OSS上文件名称，如abc/efg/123.jpg。</param>
        /// <param name="downloadFilename">下载到本地的文件的完全路径</param>
        public void GetObject(string objectName,string downloadFilename)
        {
            try
            {
                var obj = _ossClient.GetObject(_settings.BucketName, objectName);
                using (var requestStream = obj.Content)
                {
                    byte[] buf = new byte[1024];
                    var fs = File.Open(downloadFilename, FileMode.OpenOrCreate);
                    var len = 0;
                    // 通过输入流将文件的内容读取到文件或者内存中。
                    while ((len = requestStream.Read(buf, 0, 1024)) != 0)
                    {
                        fs.Write(buf, 0, len);
                    }
                    fs.Close();
                }
            }
            catch(Exception e)
            {
                _logger.LogError(e.Message);
            }
        }
        /// <summary>
        /// 判断文件是否存在
        /// </summary>
        /// <param name="objectName"></param>
        /// <returns></returns>
        public bool DoesObjectExist(string objectName)
        {
            try
            {
                return _ossClient.DoesObjectExist(_settings.BucketName, objectName);
            }
            catch(OssException ex)
            {
                _logger.LogError(ex.Message);
            }
            catch(Exception ex)
            {
                _logger.LogError(ex.Message);
            }
            return false;
        }
        /// <summary>
        /// 设置文件权限
        /// </summary>
        /// <param name="objectName"></param>
        /// <param name="cannedAccessControlList"></param>
        /// <returns></returns>
        public bool SetObjectAcl(string objectName, CannedAccessControlList cannedAccessControlList)
        {
            try
            {
                _ossClient.SetObjectAcl(_settings.BucketName, objectName, cannedAccessControlList);
                return true;
            }
            catch(Exception ex)
            {
                _logger.LogError(ex.Message);
                return false;
            }
        }
        /// <summary>
        /// 获取文件权限
        /// </summary>
        /// <param name="objectName"></param>
        /// <returns></returns>
        public AccessControlList GetObjectAcl(string objectName)
        {
            try
            {
                return _ossClient.GetObjectAcl(_settings.BucketName, objectName);
            }
            catch (OssException ex)
            {
                _logger.LogError(ex.Message);
                throw ex;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
                throw ex;
            }
        }
        /// <summary>
        /// 列举指定个数的文件 maxKeys=0时，返回默认个数100
        /// </summary>
        /// <param name="maxKeys"></param>
        public IEnumerable<OssObjectSummary> ListObjects(int maxKeys)
        {
            try
            {
                var listObjectsRequest = new ListObjectsRequest(_settings.BucketName)
                {
                    MaxKeys = maxKeys == 0 ? 100 : maxKeys
                };
                var result = _ossClient.ListObjects(listObjectsRequest);
                if(result != null)
                    return result.ObjectSummaries;
            }
            catch(Exception ex)
            {
                throw ex;
            }
            return null;
        }
        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="objectName">表示您在删除文件时需要指定的OSS上文件名称，如abc/efg/123.jpg。</param>
        /// <returns></returns>
        public bool DeleteObject(string objectName)
        {
            try
            {
                _ossClient.DeleteObject(_settings.BucketName, objectName);
                return true;
            }
            catch(Exception ex)
            {
                _logger.LogError(ex.Message);
                return false;
            }
        }
        /// <summary>
        /// 使用授权链接上传文件
        /// </summary>
        /// <param name="objectName">表示您在上传文件时需要指定的OSS上文件名称，如abc/efg/123.jpg。</param>
        /// <param name="buffer">文件字节</param>
        /// <param name="expriration">授权时间,如1小时：DateTime.Now.AddHours(1)</param>
        /// <returns></returns>
        public bool GeneratePresignedUriPutObject(string objectName,byte[] buffer,  DateTime expriration)
        {
            try
            {
                var generatePresignedUriRequest = new GeneratePresignedUriRequest(_settings.BucketName, objectName, SignHttpMethod.Put)
                {
                    Expiration = expriration,
                };
                var signedUrl = _ossClient.GeneratePresignedUri(generatePresignedUriRequest);
                // 使用签名URL上传文件。
                using (var ms = new MemoryStream(buffer))
                {
                    _ossClient.PutObject(signedUrl, ms);
                }
                return true;
            }
            catch(OssException ex)
            {
                _logger.LogError(ex.Message);
                return false;
            }
            catch(Exception ex)
            {
                _logger.LogError(ex.Message);
                return false;
            }
        }
        /// <summary>
        /// 使用授权链接下载文件
        /// </summary>
        /// <param name="objectName"></param>
        /// <returns></returns>
        public OssObject GeneratePresignedUriRequestGetObject(string objectName)
        {
            try
            {
                var metadata = _ossClient.GetObjectMetadata(_settings.BucketName, objectName);
                var etag = metadata.ETag;
                // 生成下载签名URL。
                var req = new GeneratePresignedUriRequest(_settings.BucketName, objectName, SignHttpMethod.Get);
                var uri = _ossClient.GeneratePresignedUri(req);
                // 使用签名URL下载文件。
                return _ossClient.GetObject(uri);
            }
            catch(OssException ex)
            {
                throw ex;
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// 获取授权下载链接
        /// </summary>
        /// <param name="objectName">表示您在删除文件时需要指定的OSS上文件名称，如abc/efg/123.jpg。</param>
        /// <returns></returns>
        public string GetGeneratePresignedUri(string objectName,string process = "")
        {
            try
            {
                // 生成下载签名URL。
                var req = new GeneratePresignedUriRequest(_settings.BucketName, objectName, SignHttpMethod.Get);
                if (!string.IsNullOrEmpty(_settings.Process))
                {
                    req.Process = string.IsNullOrEmpty(process) ? _settings.Process : process;
                }
                var uri = _ossClient.GeneratePresignedUri(req);
                // 使用签名URL下载文件。
                string fileUrl = "";
                string absoluteUrl = uri.AbsoluteUri;
                if (absoluteUrl.StartsWith("https"))
                    fileUrl = absoluteUrl.Replace("https://", "https://" + _settings.BucketName + ".");
                else if (absoluteUrl.StartsWith("http"))
                    fileUrl = absoluteUrl.Replace("https://", "https://" + _settings.BucketName + ".");
                else
                    throw new Exception("当前协议不支持");
                return fileUrl;
            }
            catch(OssException ex)
            {
                throw ex;
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }
    }
}
